package com.ujson.chat.server.handler;

import com.ujson.base.common.task.AsyncTask;
import com.ujson.base.common.utils.LogUtil;
import com.ujson.chat.common.proto.MessageProto;
import com.ujson.chat.server.builder.HeartbeatResponseBuilder;
import com.ujson.chat.server.session.ServerSession;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * <h3>ujson-parent</h3>
 * <p>心跳请求处理器</p>
 *
 * @author JerryLiu  a1031757312@gmail.com
 * @version 1.0
 * @date 2021-11-24 21:59
 */
@Slf4j
@Service
@ChannelHandler.Sharable
public class HeartbeatRequestHandler extends IdleStateHandler {
    @Autowired
    private HeartbeatResponseBuilder heartbeatResponseBuilder;
    // 心跳读空闲间隔
    public static final Long READ_IDLE_GAP = 1500L;
    public HeartbeatRequestHandler() {
        super(READ_IDLE_GAP, 0, 0, TimeUnit.SECONDS);
    }

    public MessageProto.MessageType getMessageType() {
        return MessageProto.MessageType.KEEPALIVE_REQUEST;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        MessageProto.Message message = null;
        if (Objects.isNull(msg)
                || !(msg instanceof MessageProto.Message)
                || (message = (MessageProto.Message) msg).getType() != getMessageType()) {
            super.channelRead(ctx, msg);
            return ;
        }

        final long sequence = message.getSequence();
        // start business handle
        LogUtil.info(log, "heart beat request read");
        AsyncTask.addTask(() -> {
            ctx.channel().writeAndFlush(heartbeatResponseBuilder.build(sequence));
        });
        super.channelRead(ctx, msg);
    }

    /**
     * 客户端心跳超过1500秒没有请求服务端, 则认为客户端掉线会调用该方法
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
        LogUtil.info(log, "client: " + ServerSession.CHANNEL_TO_USER_MAP.get(ctx.channel()) + " is outline");
        ServerSession.inst().remove(ctx.channel());
    }
}
